home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 1
/
Atari Mega Archive - Volume 1.iso
/
language
/
examples.zoo
/
numberth
/
primzahl.lsp
< prev
next >
Wrap
Lisp/Scheme
|
1991-10-22
|
41KB
|
1,038 lines
; Bruno Haible 10.11.1987-12.12.1987, 25.2.1990
; Primfaktorzerleger für ganze Zahlen
(provide 'primzahl)
;(in-package 'primzahl)
;(export '(primteiler pfzv pdivisors divisors pfz prim-von-bis
; *Kleinheit* prime probprime notprime factored *pfz-table*
; isprobprime isprime verffz setpfz))
(require 'avl) ; AVL-Bäume
(setq *print-circle* t) ; wegen der zyklischen Listen in fz-other
; (intlist a b) ergibt (a a+1 ... b-1 b), wenn a und b integers sind.
(defun intlist (a b)
(do ((l nil (cons i l))
(i b (1- i)))
((< i a) l)
) )
; exakter Quotient von Integers, schneller als / :
#-CLISP
(defun exquo (a b)
(multiple-value-bind (q r) (floor a b)
(unless (zerop r) (error "Quotient ~S/~S nicht exakt." a b))
q
) )
(require 'jacobi)
; (jacobi a b) liefert für ganze Zahlen a,b mit ungeradem b>0 das Jacobi-Symbol
; (a / b). Es ist =0 genau dann, wenn a und b nicht teilerfremd sind.
; (pfz n) liefert die Primfaktorzerlegung von n>0, die Primfaktoren von n
; in aufsteigender Reihenfolge, evtl. mehrfach vertreten.
#|
; vorläufige Version des Primfaktorzerlegers
(defun pfz (n)
(reverse
(let ((l nil)) ; l = Liste der bereits gefundenen Faktoren
(do () ((oddp n))
(setq n (exquo n 2))
(push 2 l))
(let ((p 3)) ; p = Test-teiler (ungerade)
(loop
(if (= n 1) (return l))
(setq s (isqrt n))
(do () ((or (> p s) (zerop (mod n p))))
(incf p 2))
(if (> p s) (return (cons n l)))
(push p l)
(setq n (exquo n p))
) ) ) ) )
|#
; (Miller-Rabin-test N) testet, ob eine gegebene Zahl n>1 wohl eine
; Primzahl ist.
; Falls das Ergebnis NIL ist, ist N garantiert zusammengesetzt,
; Eventuell kommt ein nichttrivialer Teiler von N als zweiter Wert heraus.
; Falls das Ergebnis T ist, ist N mit hoher Wahrscheinlichkeit prim.
; (Fehlerwahrscheinlichkeit < 1E-30 )
; Ist n gerade und >2, so ist das Ergebnis NIL.
(defun miller-rabin-test (n)
; Vorausschleife, die bereits 87% aller Zahlen faktorisiert
(dolist (p '(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67))
(if (zerop (mod n p))
(return-from miller-rabin-test (if (= n p) t (values nil p)))))
; erst jetzt geht's richtig los:
(let (blist n1 s t1)
(if (< n 2) (return-from miller-rabin-test nil))
(setq blist (cond ((< n 2000) '(2))
((< n 1300000) '(2 3))
((< n 25000000) '(2 3 5))
((< n 3200000000) '(2 3 5 7))
(t '(2 3 5 7 11 13 17 19 23 29
31 37 41 43 47 53 59 61 67 71
73 79 83 89 97 101 103 107 109 113
127 131 137 139 149 151 157 163 167 173
179 181 191 193 197 199 211 223 227 229))))
; mit 50 Primzahlen: P(falsch) < (1/4)^50 < 1E-30
(setq n1 (- n 1))
(setq s n1 t1 0)
(do () ((oddp s))
(setq s (exquo s 2)) (incf t1))
; n-1 = s * 2^t1 mit ungeradem s zerlegt.
(dolist (b blist)
(if (zerop (mod n b))
(if (= n b) (return-from miller-rabin-test t)
(return-from miller-rabin-test (values nil b))))
(do ((c (exptmod (mod b n) s n) (sqrmod c n))
(c1 n1 c)
(j 0 (1+ j)))
((or (= j t1) (= c 1))
(cond ((not (= c 1)) ; sicher j=t1, aber c/=1
(return-from miller-rabin-test nil))
((not (= c1 n1)) ; sicher c=1, c1/=n-1, also j>0
; ggt(c1-1,n) * ggt(c1+1,n) = ggt(c1^2-1,n) = ggt(c-1,n) = n
(return-from miller-rabin-test (values nil (gcd n (- c1 1)))))
) )) ; Eine Iteration über b beendet
) ; blist abgearbeitet
t ; n wahrscheinlich prim
) )
; Hilfsfunktion für Miller-Rabin-Test:
; Quadrieren modulo n von x mit 0<=x<n
(defun sqrmod (x n)
(mod (* x x) n))
; Hilfsfunktion für Miller-Rabin-Test:
; Potenzieren modulo n von a mit 0<=a<n und b>0
(defun exptmod (a b n)
(let ((c 1))
(loop ; a^b*c mod n bleibt invariant
(if (oddp b) (setq c (mod (* a c) n)))
(if (= b 1) (return c))
(setq a (mod (* a a) n) b (floor b 2))
) ) )
; verifizierter Primzahltest für kleine Zahlen n>0
(defun klprimtest (n)
(cond ((= n 1) nil)
((evenp n) (= n 2))
(t (let ((s (isqrt n)))
(do ((i 3 (+ i 2)))
((> i s) t)
(if (zerop (mod n i)) (return nil))
) ) ) ) )
; Primfaktorzerlegung kleiner Zahlen n>0
(defun klpfz (n)
(let ((l nil))
(do () ((oddp n)) (setq n (ash n -1)) (push 2 l)) ; Zweier heraus
(block Rest-prim
(do ((p 3))
((= n 1))
(loop ; in dieser Schleife wird ein Teiler gesucht
(cond ((> (* p p) n) (return-from Rest-prim (push n l)))
((zerop (mod n p)) (push p l) (setq n (exquo n p)) (return))
(t (incf p 2))
) ) ) )
(nreverse l)
) )
; Datenstruktur: In *pfz-table* steht eine Tabelle von Primfaktorzerlegungs-
; ergebnissen. Zu jeder Zahl (sie ist der Schlüssel) steht drin:
; Ob sie Primzahl ist, eine eventuelle Faktorzerlegung bzw. evtl. eine
; Primitivwurzel.
(defparameter *Kleinheit* 10000
"Nur Zahlen >= *Kleinheit* werden in die PFZ-Tabelle aufgenommen.")
; *Kleinheit* sollte nur geändert werden, wenn gleichzeitig
; (setq *pfz-table* nil) ausgeführt wird!
(assert (> *Kleinheit* 1))
(deftype fz-class () "Das Ergebnis einer Faktorzerlegung, ganz grob"
'(member ; ein Symbol:
nil ; (nur kurz nach der Initialisierung)
prime ; n ist prim
probprime ; n ist wahrscheinlich prim
notprime ; n ist garantiert zusammengesetzt, Faktoren unbekannt
factored ; n ist zusammengesetzt, schon faktorisiert
) )
(defstruct fz "Eine Faktorisierung als Ganzes"
(num 2 :type integer :read-only t) ; die zu faktorisierende Zahl, >1
(cl nil :type fz-class) ; Klassifikation
(other nil) ; weitere Information:
; bei cl = prime : NIL oder eine Primitivwurzel mod n
; bei cl = probprime : Eine zyklische Liste von wartenden Funktionen.
; Jede von ihnen liefert NIL, wenn sie wieder aufge-
; rufen werden will, und (NIL . factor), falls sie
; die Zahl als zusammengesetzt nachgewiesen hat, evtl.
; einen nichttrivialen Faktor gefunden hat, und
; (T . Prw), falls sie die Zahl als prim nachgewiesen
; hat, mit evtl. einer Primitivwurzel Prw.
; (Eventuell zu Beginn die leere Liste.)
; bei cl = notprime : Eine zyklische Liste von wartenden Funktionen
; (Closures), die mitten in der Suche nach einem
; Faktor von n stecken. Jede von ihnen liefert NIL,
; wenn sie wieder aufgerufen werden will, und einen
; nichttrivialen Faktor, wenn sie einen gefunden hat.
; (Eventuell zu Beginn die leere Liste.)
; bei cl = factored : Ein Konstrukt vom Typ fzl, das die Faktorenzerlegung
; von n widerspiegelt.
)
(defstruct fzl "Faktorenzerlegung, Liste"
(allprimes nil :type symbol) ; Flag, ob alle Faktoren von factorlist
; bekanntermaßen Primzahlen sind.
(factorlist nil :type list) ; Eine Liste (f1 ... fk) von Faktoren von n,
; mit 1 < f1 <= ... <= fk und n = f1 * ... * fk. Die fj sind dabei
; (Pointer auf) Konstrukte vom Typ fz, bzw. bei fj<*Kleinheit* ist fj
; die Zahl selbst und Primzahl.
)
; (fz-key f) ergibt zu einem Element einer Faktorzerlegungsliste die
; wirkliche Zahl.
(defun fz-key (g)
(if (integerp g) g (fz-num g))
)
; (fz-isprime f) stellt fest, ob ein Element einer Faktorzerlegungsliste
; prim ist.